using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Dm;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Dm.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Dm.Storage.Internal;

public class DmDatabaseCreator : RelationalDatabaseCreator
{
	private readonly IDmRelationalConnection _connection;

	private readonly IRawSqlCommandBuilder _rawSqlCommandBuilder;

	public virtual TimeSpan RetryDelay { get; set; } = TimeSpan.FromMilliseconds(500.0);


	public virtual TimeSpan RetryTimeout { get; set; } = TimeSpan.FromMinutes(1.0);


	public DmDatabaseCreator([NotNull] RelationalDatabaseCreatorDependencies dependencies, [NotNull] IDmRelationalConnection connection, [NotNull] IRawSqlCommandBuilder rawSqlCommandBuilder)
		: base(dependencies)
	{
		_connection = connection;
		_rawSqlCommandBuilder = rawSqlCommandBuilder;
	}

	public override void Create()
	{
		DmConnectionStringBuilder dmConnectionStringBuilder = new DmConnectionStringBuilder(this._connection.ConnectionString);
		dmConnectionStringBuilder.Schema = null;
		bool flag = true;
		using (DmConnection dmConnection = new DmConnection(dmConnectionStringBuilder.ConnectionString, true))
		{
			try
			{
				dmConnection.Open();
			}
			catch (Exception)
			{
				flag = false;
			}
		}
		using (IDmRelationalConnection dmRelationalConnection = this._connection.CreateMasterConnection())
		{
			this.Dependencies.MigrationCommandExecutor.ExecuteNonQuery(flag ? this.CreateCreateSchemaOperations() : this.CreateCreateOperations(), dmRelationalConnection);
		}

	}

	public override async Task CreateAsync(CancellationToken cancellationToken = default(CancellationToken))
	{
		using (IDmRelationalConnection masterConnection = this._connection.CreateMasterConnection())
		{
			await this.Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(this.CreateCreateOperations(), masterConnection, cancellationToken);
		}
		//IDmRelationalConnection masterConnection = null;

	}

	public override bool HasTables()
	{
		return ExecutionStrategyExtensions.Execute<IDmRelationalConnection, bool>(this.Dependencies.ExecutionStrategy, this._connection, 
			(IDmRelationalConnection connection) => Convert.ToInt64(this.CreateHasTablesCommand()
			.ExecuteScalar(new RelationalCommandParameterObject(connection, null, null, this.Dependencies.CurrentContext.Context, this.Dependencies.CommandLogger))) != 0L);


	}

	public override Task<bool> HasTablesAsync(CancellationToken cancellationToken = default(CancellationToken))
	{
		return ExecutionStrategyExtensions.ExecuteAsync<IDmRelationalConnection, bool>(this.Dependencies.ExecutionStrategy, _connection, (Func<IDmRelationalConnection, CancellationToken, Task<bool>>)(async (IDmRelationalConnection connection, CancellationToken ct) => (int)(await CreateHasTablesCommand().ExecuteScalarAsync(new RelationalCommandParameterObject(connection, null, null, this.Dependencies.CurrentContext.Context, this.Dependencies.CommandLogger), ct)) != 0), cancellationToken);
	
	
	}

	private IRelationalCommand CreateHasTablesCommand()
	{
		return _rawSqlCommandBuilder.Build("SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END FROM SYS.SYSOBJECTS WHERE SCHID = CURRENT_SCHID() AND TYPE$='SCHOBJ' AND SUBTYPE$='UTAB'");
	}

	public override void CreateTables()
	{
		try
		{
			IReadOnlyList<MigrationCommand> createTablesCommands = this.GetCreateTablesCommands((MigrationsSqlGenerationOptions)0);
			this.Dependencies.MigrationCommandExecutor.ExecuteNonQuery(createTablesCommands, this.Dependencies.Connection);
		}
		catch (Exception ex)
		{
			throw ex;
		}
	}

	private IReadOnlyList<MigrationCommand> CreateCreateOperations()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		DmConnectionStringBuilder val = new DmConnectionStringBuilder(((IRelationalConnection)_connection).DbConnection.ConnectionString);
		return this.Dependencies.MigrationsSqlGenerator.Generate((IReadOnlyList<MigrationOperation>)(object)new DmCreateUserOperation[1]
		{
			new DmCreateUserOperation
			{
				UserName = val.User,
				Password = val.Password,
				Schema = val.Schema
			}
		}, (IModel)null, (MigrationsSqlGenerationOptions)0);
	}

	private IReadOnlyList<MigrationCommand> CreateCreateSchemaOperations()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		DmConnectionStringBuilder val = new DmConnectionStringBuilder(((IRelationalConnection)_connection).DbConnection.ConnectionString);
		return this.Dependencies.MigrationsSqlGenerator.Generate((IReadOnlyList<MigrationOperation>)(object)new DmCreateSchemaOperation[1]
		{
			new DmCreateSchemaOperation
			{
				UserName = val.User,
				Schema = val.Schema
			}
		}, (IModel)null, (MigrationsSqlGenerationOptions)0);
	}

	public override bool Exists()
	{
		try
		{
			((IRelationalConnection)_connection).Open(true);
			((IRelationalConnection)_connection).Close();
			return true;
		}
		catch (DmException)
		{
			((IRelationalConnection)_connection).DbConnection.Close();
			return false;
		}
	}

	public override async Task<bool> ExistsAsync(CancellationToken cancellationToken = default(CancellationToken))
	{
		try
		{
			await ((IRelationalConnection)_connection).OpenAsync(cancellationToken, true);
			((IRelationalConnection)_connection).Close();
			return true;
		}
		catch (DmException)
		{
			return false;
		}
	}

	private static bool IsDoesNotExist(DmException exception)
	{
		if (exception.Number != 4060 && exception.Number != 1832)
		{
			return exception.Number == 5120;
		}
		return true;
	}

	private bool RetryOnExistsFailure(DmException exception)
	{
		if (exception.Number == 233 || exception.Number == -2 || exception.Number == 4060 || exception.Number == 1832 || exception.Number == 5120)
		{
			return true;
		}
		return false;
	}

	public override void Delete()
	{
		using IDmRelationalConnection dmRelationalConnection = _connection.CreateMasterConnection();
		try
		{
			this.Dependencies.MigrationCommandExecutor.ExecuteNonQuery((IEnumerable<MigrationCommand>)CreateDropSchemaCommands(), (IRelationalConnection)dmRelationalConnection);
		}
		catch (Exception)
		{
			this.Dependencies.MigrationCommandExecutor.ExecuteNonQuery((IEnumerable<MigrationCommand>)CreateDropUserCommands(), (IRelationalConnection)dmRelationalConnection);
		}
	}

	public override async Task DeleteAsync(CancellationToken cancellationToken = default(CancellationToken))
	{
		using IDmRelationalConnection masterConnection = _connection.CreateMasterConnection();
		try
		{
			await this.Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync((IEnumerable<MigrationCommand>)CreateDropSchemaCommands(), (IRelationalConnection)masterConnection, cancellationToken);
		}
		catch (Exception ex)
		{
			_ = ex;
			await this.Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync((IEnumerable<MigrationCommand>)CreateDropUserCommands(), (IRelationalConnection)masterConnection, cancellationToken);
		}
	}

	private IReadOnlyList<MigrationCommand> CreateDropSchemaCommands()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		string schema = new DmConnectionStringBuilder(((IRelationalConnection)_connection).DbConnection.ConnectionString).Schema;
		if (string.IsNullOrEmpty(schema))
		{
			throw new InvalidOperationException(DmStrings.NoUserId);
		}
		MigrationOperation[] array = (MigrationOperation[])(object)new MigrationOperation[1]
		{
			new DmDropSchemaOperation
			{
				Schema = schema
			}
		};
		return this.Dependencies.MigrationsSqlGenerator.Generate((IReadOnlyList<MigrationOperation>)array, (IModel)null, (MigrationsSqlGenerationOptions)0);
	}

	private IReadOnlyList<MigrationCommand> CreateDropUserCommands()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		string user = new DmConnectionStringBuilder(((IRelationalConnection)_connection).DbConnection.ConnectionString).User;
		if (string.IsNullOrEmpty(user))
		{
			throw new InvalidOperationException(DmStrings.NoUserId);
		}
		MigrationOperation[] array = (MigrationOperation[])(object)new MigrationOperation[1]
		{
			new DmDropUserOperation
			{
				UserName = user
			}
		};
		return this.Dependencies.MigrationsSqlGenerator.Generate((IReadOnlyList<MigrationOperation>)array, (IModel)null, (MigrationsSqlGenerationOptions)0);
	}
}
